﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using MathNet.Numerics;
using MathNet.Numerics.IntegralTransforms;
using MathNet.Spatial.Euclidean;

namespace CrankShaftResultHandler
{
    public class PointMeasureConfig
    {
        public class Tolerances
        {
            public double dx_norminal { get; set; } // 偏心距名义尺寸
            public double dx_tolerance { get; set; } // 偏心距公差
            public double d_up { get; set; } // 圆锥圆柱上偏差
            public double d_down { get; set; } // 圆锥圆柱下偏差
            public double d2_up { get; set; } // 偏心圆柱上偏差
            public double d2_down { get; set; } // 偏心圆柱下偏差
            public double cylinder { get; set; } // 圆柱度
            public double ang_toleracne { get; set; } // 角度公差
            public void CopyTo(ref Tolerances target)
            {
                target.dx_norminal = dx_norminal;
                target.dx_tolerance = dx_tolerance;
                target.d_up = d_up;
                target.d_down = d_down;
                target.d2_up = d2_up;
                target.d2_down = d2_down;
                target.cylinder = cylinder;
                target.ang_toleracne = ang_toleracne;
            }
        }
        public class Configs
        {
            public int P0 { get; set; }
            public int P1 { get; set; }
            public int P2 { get; set; }
            public int P3 { get; set; }
            public int P4 { get; set; }
            public int P5 { get; set; }
            public int P6 { get; set; }
            public int P7 { get; set; }
            public double UpperLimit { get; set; }
            public double LowerLimit { get; set; }
            public Tolerances Tolerance { get; set; } = new Tolerances();
            public string RefrenceReport { get; set; }
        }

        public string CurrentType { get; set; }
        public Configs RV20E { get; set; }
        public Configs RV40E { get; set; }
        public Configs RV80E { get; set; }
        public Configs RV100C { get; set; }

    }

    public class Calibration
    {
        public const double SphericalR = 1600;
        public const double GearConst = 36; //花键角度
        // 校准值
        public List<double> dOffset { get; set; } = new List<double>(new double[] {
                0, 0, 0, 0, 0, 0, 0, 0
            });
        // 相位校准值
        public List<double> phaseOffset { get; set; } = new List<double>(new double[]
        {
                0, 0,
        });
        public double AngleTwoOffset { get; set; }
        public List<double> DiameterRatio { get; set; } = new List<double>(new double[] { 1.0, 1.0, 1.0, 1.0 });
        public double AngleRatio { get; set; } = 1.0;
    }

    public class HexagonReport
    {
        public double DAT2_LOW { get; set; } // 对应d0
        public double DAT2_UP { get; set; } // 对应d1
        public double ECC2_LOW { get; set; }
        public double ECC2_UP { get; set; }
        public double ECC1_LOW { get; set; }
        public double ECC1_UP { get; set; }
        public double DAT1_LOW { get; set; }
        public double DAT1_UP { get; set; }
        public double ANGLE_4_ { get; set; }
        public double ANGLE_ONE { get; set; }
        public double ANGLE_TWO { get; set; }
        public double CENTER_2 { get; set; }
        public double d4 { get; set; }
        public double d6 { get; set; }
        public double d7 { get; set; }
        public double d5 { get; set; }
        public double A1_DX { get; set; }
        public double A2_DX { get; set; }
    }

    public class ResultHandler
    {
        /**
         * 曲柄轴测量圆0~7
         *          ____
         *         | 7  |
         *     ____|_6__|_
         *    |      5    |
         *    |______4____|_
         *       |   3      |
         *       |___2______|
         *         | 1  |
         *         | 0  |
         * */
        public class CurvePoint
        {
            public int index { get; set; }
            public string PointMark { get; set; }
            public double X { get; set; }
            public double Y { get; set; }
            public double Z { get; set; }
            public double p { get; set; }
            public double Phi { get; set; }

            public static CurvePoint operator +(CurvePoint p1, CurvePoint p2)
            {
                return new CurvePoint
                {
                    X = p1.X + p2.X,
                    Y = p1.Y + p2.Y,
                    Z = p1.Z + p2.Z,
                };
            }

            public static CurvePoint operator -(CurvePoint p1, CurvePoint p2)
            {
                return new CurvePoint
                {
                    X = p1.X - p2.X,
                    Y = p1.Y - p2.Y,
                    Z = p1.Z - p2.Z,
                };
            }

            public static CurvePoint operator /(CurvePoint p1, double p2)
            {
                return new CurvePoint
                {
                    X = p1.X / p2,
                    Y = p1.Y / p2,
                    Z = p1.Z / p2,
                };
            }

            public double[] ToArray()
            {
                return new double[3] { X, Y, Z };
            }

            public Vector3D ToVector3D()
            {
                return new Vector3D(X, Y, Z);
            }

            public CurvePoint GetCopy()
            {
                var ret = new CurvePoint();
                ret.X = X;
                ret.Y = Y;
                ret.Z = Z;
                ret.p = p;
                ret.Phi = Phi;
                ret.index = index;
                ret.PointMark = PointMark;
                return ret;
            }
        }

        public class CurveLine
        {
            public CurvePoint start { get; set; }
            public CurvePoint end { get; set; }
            public void SetPoint(CurvePoint p1, CurvePoint p2)
            {
                start = p1;
                end = p2;
            }
        }
        public List<CurvePoint>[] CloudsCollectionBackup = new List<CurvePoint>[8];  // 备份数据防止后面再要用源数据
        public List<CurvePoint>[] CloudsCollectionOriginal = new List<CurvePoint>[8];
        public List<CurvePoint>[] CloudsCollectionPeakRemoved = new List<CurvePoint>[8];
        public List<CurvePoint>[] CloudsCollectionForFFT = new List<CurvePoint>[8];
        public List<CurvePoint>[] CloudsOffsetVsRadians = new List<CurvePoint>[8];
        public CurvePoint[] CenterPoints = new CurvePoint[8];
        public CurvePoint[] Pds = new CurvePoint[8];
        public double[] Zmeans = new double[8];
        public double[] Radius = new double[8];
        public double[] Coaxialitys = new double[8]; // 同轴度
        public double[] c = new double[8]; // 圆度
        public double[] MT2571ValueMean = new double[8]; // 源数据
        public List<double>[] CircleDiameterList = new List<double>[8];
        public List<double>[] CylinderDiameterList = new List<double>[8];
        public List<int>[] PeakIndexList = new List<int>[8];
        public CurvePoint center_p01;
        public CurvePoint center_p23;
        public CurvePoint center_p45;
        public CurvePoint center_p67;
        public CurveLine center_line0167 = new CurveLine();
        public CurveLine center_line01 = new CurveLine();
        public CurveLine center_line23 = new CurveLine();
        public CurveLine center_line45 = new CurveLine();
        public CurveLine center_line67 = new CurveLine();

        public Calibration CAL = new Calibration();
        public HexagonReport Refrence = new HexagonReport();

        // 公差配置
        public static PointMeasureConfig.Tolerances Tolerance = new PointMeasureConfig.Tolerances();

        [Description("检测报告日期")]
        public string MeasureDate { get; set; }
        public string UUID { get; set; }
        [Description("校准文件的路径")]
        public string CALFilePath { get; set; } = "../../Docs/calibration.json";
        [Description("上下两个圆柱的同轴度")]
        public double Coaxiality { get; set; }
        [Description("两个曲柄的偏心距")]
        public double[] CircleDx { get; set; } = new double[2];

        private double _Ang; // 未校准的
        [Description("偏心圆的夹角(degs)")]
        public double Ang { get; set; }

        private double _AngleTwoDeg; // 未校准的
        [Description("图纸上的AngleTwo")]
        public double AngleTwoDeg { get; set; }
        public string AngleTwoDms { get; set; }

        [Description("偏心圆的夹角(度分秒)")]
        public string AngDms { get; set; }
        [Description("相位角(degs)")]

        private double[] _PhaseDeg = new double[2];
        public double[] PhaseDeg { get; set; } = new double[2];
        public string Phase0Dms { get; set; }
        public string Phase1Dms { get; set; }
        [Description("零件安装相位角(rad)")]
        public double[] PartPhaseRad { get; set; } = new double[2];
        [Description("圆柱度")]
        public double[] Cylinders { get; set; } = new double[4];
        [Description("直径比例，在FFT滤波的时候会有幅值损失")]
        public double[] DiameterRatio { get; private set; } = new double[4] { 1.0,1.0,1.0,1.0 };
        [Description("直径0")]
        public double Diameter0 { get { return Radius[0] + Radius[1] - 2 * Calibration.SphericalR; } }
        public double d4 { get { return Diameter0 * CAL.DiameterRatio[0]; } }
        [Description("直径1")]
        public double Diameter1 { get { return Radius[2] + Radius[3] - 2 * Calibration.SphericalR; } }
        public double d6 { get { return Diameter1 * CAL.DiameterRatio[1]; } }
        [Description("直径2")]
        public double Diameter2 { get { return Radius[4] + Radius[5] - 2 * Calibration.SphericalR; } }
        public double d7 { get { return Diameter2 * CAL.DiameterRatio[2]; } }
        [Description("直径3")]
        public double Diameter3 { get { return Radius[6] + Radius[7] - 2 * Calibration.SphericalR; } }
        public double d5 { get { return Diameter3 * CAL.DiameterRatio[3]; } }
        [Description("数据是否符合要求")]
        public int[] IsValueOk { get; private set; } = new int[18];

        /// <summary>
        /// 花键侧支撑圆相对公共轴线同轴度
        /// </summary>
        public double g1 { get; private set; }

        /// <summary>
        /// 非花键侧支撑圆相对公共轴线同轴度
        /// </summary>
        public double g3 { get; private set; }
        enum ValueJudge
        {
            NG = 0,
            Warning = 1,
            FullyOK = 2
        }

        public ResultHandler()
        {
            for(int i=0;i<CloudsCollectionOriginal.Length;i++)
            {
                CloudsCollectionOriginal[i] = new List<CurvePoint>();
                CloudsCollectionBackup[i] = new List<CurvePoint>();
                CloudsCollectionPeakRemoved[i] = new List<CurvePoint>();
                CloudsOffsetVsRadians[i] = new List<CurvePoint>();
                CylinderDiameterList[i] = new List<double>();
                Pds[i] = new CurvePoint();
            }
        }

        public string GetTitle()
        {
            return "UUID,d4,d6,d7,d5,a1,a2,g1_3,c1,c3,c4,c2,ANGLE(4),Phase0Dms,Phase1Dms,ANGLE_two,IsOK,Date";
        }

        public string GetChineseTitle()
        {
            return "曲柄轴编号,花键侧圆锥圆直径d4(mm),花键侧偏心圆直径d6(mm),非花键侧偏心圆直径d7(mm),非花键侧圆锥圆直径d5(mm)," +
                "花键侧偏心距a1(mm),非花键侧偏心距a2(mm),支承圆相对于公共轴线同轴度g1_3(mm)," +
                "花键侧圆锥圆圆柱度c1(mm),花键侧偏心圆圆柱度c3(mm),非花键侧偏心圆圆柱度c4(mm),非花键侧圆锥圆圆柱度c2(mm)," +
                "两偏心夹角ANGLE(4)(度.分.秒),花键侧偏心圆摆放角度(度.分.秒),非花键侧偏心圆摆放角度(度.分.秒),花键与两偏心夹角ANGLE_two(度.分.秒)," +
                "初步判定是否OK,报告计算日期";
        }

        public override string ToString()
        {
            var t = DateTime.Now.ToString("yyyy-MM-dd");
            int ok = 1;
            string OK_NG = "OK";
            if (MeasureDate.Length == 0) MeasureDate = t;
            for(int i=0;i<IsValueOk.Length;i++)
            {
                ok = ok * IsValueOk[i];
            }
            if (ok == 0) OK_NG = "NG";
            return $"{UUID},{(d4 / 1000.0):F4},{(d6 / 1000.0):F4},{(d7 / 1000.0):F4},{(d5 / 1000.0):F4}," +
                $"{(CircleDx[0] / 1000.0):F4},{(CircleDx[1] / 1000.0):F4},{(Coaxiality / 1000.0):F4},{(Cylinders[0] / 1000.0):F4},{(Cylinders[1] / 1000.0):F4},{(Cylinders[2] / 1000.0):F4},{(Cylinders[3] / 1000.0):F4},"
                + $"{AngDms},{Phase0Dms},{Phase1Dms},{AngleTwoDms},{OK_NG},{MeasureDate}";
        }

        public void SaveResult(string uuid, string history_path=null, bool append=true)
        {
            UUID = uuid;
            var p = GetHistoryFilePath();
            if (history_path != null) p = history_path;
            bool new_file = true;
            if (File.Exists(p)) new_file = false;
            using (var file = new StreamWriter(p, append))
            {
                if (new_file) file.WriteLine(GetChineseTitle());
                file.WriteLine(ToString());
            }
        }

        public string GetHistoryFilePath()
        {
            string t = DateTime.Now.ToString("yyyy-MM-dd");
            var dir = Path.GetDirectoryName(CALFilePath);
            var path = Path.Combine(dir, $"history/history_{t}.csv");
            return path;
        }

        public ResultHandler ReadCSV(string file_name)
        {
            if (!File.Exists(file_name))
                return null;
            foreach (var c in CloudsCollectionOriginal) c.Clear();
            foreach (var c in CloudsCollectionBackup) c.Clear();
            var name = Path.GetFileName(file_name);
            var lines = File.ReadAllLines(file_name).Skip(2);
            string measure_date = "";
            foreach(var line in lines)
            { 
                string[] values = line.Split(',');
                measure_date = values.Last();
                int i = Convert.ToInt32(values[1].Remove(0, 1));
                var p = new CurvePoint()
                {
                    index = Convert.ToInt32(values[0]),
                    PointMark = values[1],
                    p = Convert.ToDouble(values[2]),
                    Z = Convert.ToDouble(values[3]),
                    Phi = Convert.ToDouble(values[4])
                };
                CloudsCollectionOriginal[i].Add(p);
                CloudsCollectionBackup[i].Add(p.GetCopy());
            };
            MeasureDate = measure_date;
            Console.WriteLine($"File {name} Load Successfully.");
            return this;
        }

        public ResultHandler SaveCAL(string p = null)
        {
            var path = p ?? CALFilePath;
            using(var file = new StreamWriter(path, false))
            {
                var option = new JsonSerializerOptions();
                option.WriteIndented = true;
                var s = JsonSerializer.Serialize(CAL, option);
                file.Write(s);
            }
            return this;
        }

        /// <summary>
        /// 在指定文件中加入标定信息
        /// </summary>
        /// <param name="path"></param>
        public void WriteCalibrationHeader(string path=null)
        {
            if (path is null)
                path = GetHistoryFilePath();
            using(var file = new StreamWriter(path, append:true))
            {
                file.WriteLine(@"/************************** CALIBRATION ******************************/");
                var option = new JsonSerializerOptions();
                option.WriteIndented = true;
                var s = JsonSerializer.Serialize(CAL, option);
                file.Write(s);
                file.WriteLine(DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"));
                file.WriteLine("/*********************************************************************/");
                file.WriteLine(GetChineseTitle());
            }
        }

        public ResultHandler ReCalibration()
        {
            double[] ds = new double[8]
            {
                Refrence.DAT2_LOW,
                Refrence.DAT2_UP,
                Refrence.ECC2_LOW,
                Refrence.ECC2_UP,
                Refrence.ECC1_LOW,
                Refrence.ECC1_UP,
                Refrence.DAT1_LOW,
                Refrence.DAT1_UP
            };
            var c2 = CloudsCollectionBackup[2].Select(p =>
            {
                var a = p.Phi + PartPhaseRad[0];
                var r2 = Refrence.ECC2_LOW / 2 + Calibration.SphericalR;
                var dx = Math.Abs(Refrence.A2_DX);
                var h2 = Math.Abs(p.p)  + Math.Sqrt(Math.Pow(r2, 2) - Math.Pow(dx * Math.Sin(a), 2)) + dx * Math.Cos(a);
                return h2;
            }).ToArray();
            var c3 = CloudsCollectionBackup[3].Select(p =>
            {
                var a = p.Phi + PartPhaseRad[0];
                var r3 = Refrence.ECC2_UP / 2 + Calibration.SphericalR;
                var dx = Math.Abs(Refrence.A2_DX);
                var h3 = Math.Abs(p.p) + Math.Sqrt(Math.Pow(r3, 2) - Math.Pow(dx * Math.Sin(a), 2)) + dx * Math.Cos(a);
                return h3;
            }).ToArray();
            var c4 = CloudsCollectionBackup[4].Select(p =>
            {
                var a = p.Phi + PartPhaseRad[1];
                var r4 = Refrence.ECC1_LOW / 2 + Calibration.SphericalR;
                var dx = Math.Abs(Refrence.A1_DX);
                var h4 = Math.Abs(p.p) + Math.Sqrt(Math.Pow(r4, 2) - Math.Pow(dx * Math.Sin(a), 2)) + dx * Math.Cos(a);
                return h4;
            }).ToArray();
            var c5 = CloudsCollectionBackup[5].Select(p =>
            {
                var a = p.Phi + PartPhaseRad[1];
                var r5 = Refrence.ECC1_UP / 2 + Calibration.SphericalR;
                var dx = Math.Abs(Refrence.A1_DX);
                var h5 = Math.Abs(p.p) + Math.Sqrt(Math.Pow(r5, 2) - Math.Pow(dx * Math.Sin(a), 2)) + dx * Math.Cos(a);
                return h5;
            }).ToArray();
            for (int i = 0; i < CAL.dOffset.Count; i++)
            {
                CAL.dOffset[i] = ds[i] / 2.0 - MT2571ValueMean[i] + Calibration.SphericalR;
            }
            CAL.dOffset[2] = c2.Average();
            CAL.dOffset[3] = c3.Average();
            CAL.dOffset[4] = c4.Average();
            CAL.dOffset[5] = c5.Average();
            CAL.AngleRatio = Refrence.ANGLE_4_ / _Ang;
            // 2022-06-13 Center2表征了angle2的正负，按理来说不用除以2，但是实际计算的时候发现如果Refrence.ANGLE_TWO
            // 前面不加负号，测出来的全是正偏差的，前面加负号测出来的全是负偏差的
            CAL.AngleTwoOffset = -Refrence.ANGLE_TWO * Math.Sign(Refrence.CENTER_2) - _AngleTwoDeg;

            // 一开始对图纸的理解有误
            CAL.phaseOffset[0] = Refrence.ANGLE_TWO - _PhaseDeg[0];
            CAL.phaseOffset[1] = Refrence.ANGLE_ONE - _PhaseDeg[1];
            return this;
        }

        public void LoadRefrenceAndConfig(string refrence_path, string p = null)
        {
            var path = p ?? CALFilePath;
            CALFilePath = path;
            LoadCAL(path);
            LoadRefrenceFile(refrence_path);
        }

        private void LoadCAL(string path)
        {
            if (!File.Exists(path))
            {
                Console.WriteLine($"Cant Find File: {path}");
                return;
            }
            string s = File.ReadAllText(path);
            try
            {
                CAL = JsonSerializer.Deserialize<Calibration>(s);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        private double GetDiameterFromLine(string line)
        {
            // 三坐标报告的单位是mm
            return 1000 * Convert.ToDouble(line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1]);
        }

        private double GetNominalFromLine(string line)
        {
            return 1000 * Convert.ToDouble(line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[2]);
        }

        private double GetAngleFromeLine(string line)
        {
            var str = line.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries)[1];
            var dms = str.Split('.');
            int d = Convert.ToInt32(dms[0]);
            int m = Convert.ToInt32(dms[1]);
            int s = Convert.ToInt32(dms[2]);
            return d + m / 60.0 + s / 3600.0;
        }

        private void LoadRefrenceFile(string path)
        {
            if (!File.Exists(path))
            {
                Console.WriteLine($"Cant Fint File: {path}");
                return;
            }
            var lines = File.ReadAllLines(path);

            // 如果发现20E，40E报告的行数有差别要手动修改报告，后面的报告少了g2所以行数有差别
            Refrence.d4 = GetDiameterFromLine(lines[21]);
            Refrence.d5 = GetDiameterFromLine(lines[27]);
            Refrence.d6 = GetDiameterFromLine(lines[33]);
            Refrence.d7 = GetDiameterFromLine(lines[39]);
            Refrence.A1_DX = GetNominalFromLine(lines[61]);
            Refrence.A2_DX = Refrence.A1_DX;
            Refrence.ANGLE_ONE = GetAngleFromeLine(lines[105]);
            Refrence.ANGLE_TWO = GetAngleFromeLine(lines[109]);
            Refrence.CENTER_2 = GetDiameterFromLine(lines[113]);
            Refrence.ANGLE_4_ = GetAngleFromeLine(lines[101]);
            Refrence.DAT1_UP = GetDiameterFromLine(lines[117]);
            Refrence.DAT1_LOW = GetDiameterFromLine(lines[121]);
            Refrence.DAT2_UP = GetDiameterFromLine(lines[125]);
            Refrence.DAT2_LOW = GetDiameterFromLine(lines[129]);
            Refrence.ECC1_UP = GetDiameterFromLine(lines[133]);
            Refrence.ECC1_LOW = GetDiameterFromLine(lines[137]);
            Refrence.ECC2_UP = GetDiameterFromLine(lines[141]);
            Refrence.ECC2_LOW = GetDiameterFromLine(lines[145]);
        }

        public void SortClouds()
        {
            for (int i = 0; i < CloudsCollectionOriginal.Length; i++)
            {
                if(i<2||i>5)
                    MT2571ValueMean[i] = CloudsCollectionOriginal[i].Average(p => p.p);
                else
                {
                    ;// 偏心圆的源数据均值没有意义。要折算到轴线距离值。所以这里不处理了，再得知相位后再计算
                }
                CloudsCollectionOriginal[i].ForEach(p => p.p += CAL.dOffset[i]);
                CloudsCollectionOriginal[i] = CloudsCollectionOriginal[i].OrderBy(p => p.Phi).ToList();
                Zmeans[i] = CloudsCollectionOriginal[i].Average(p => p.Z);
            }
        }

        public void PolarToRectangular()
        {
            for (int i = 0; i < CloudsCollectionOriginal.Length; i++)
            {
                CloudsCollectionOriginal[i].ForEach(p => { 
                    p.X = Math.Abs(p.p) * Math.Cos(p.Phi);
                    p.Y = Math.Abs(p.p) * Math.Sin(p.Phi);
                });
            }
        }

        public void FitCircle(ref List<CurvePoint>[] curves)
        {
            for (int i = 0; i < curves.Length; i++)
            {
                CenterPoints[i] = MathHelper.CircleFit(curves[i], ref Radius[i]);
                CenterPoints[i].Z = Zmeans[i];
                Console.WriteLine($"Diameter At Z:{Zmeans[i]} is {0.002 * (Radius[i] - Calibration.SphericalR)}");
            }
        }

        public void GenerateDiameterList(ref List<CurvePoint>[] curves)
        {
            for (int i = 0; i < curves.Length; i++)
            {
                CircleDiameterList[i] = curves[i].Select(p =>
                {
                    var d = Math.Sqrt(Math.Pow(p.X - CenterPoints[i].X, 2) + Math.Pow(p.Y - CenterPoints[i].Y, 2));
                    return d;
                }).ToList();
            }
        }

        public void RemovePeak()
        {
            for (int i = 0; i < CloudsCollectionOriginal.Length; i++)
            {
                var diameter_array = new double[CircleDiameterList[i].Count];
                CircleDiameterList[i].CopyTo(diameter_array);
                PeakIndexList[i] = GetPeakIndex(CircleDiameterList[i]);
                CircleDiameterList[i].Clear(); // 清除原有数据换上新数据
                CloudsCollectionPeakRemoved[i].Clear();
                for (int j = 0; j< CloudsCollectionOriginal[i].Count; j++)
                {
                    if (!PeakIndexList[i].Contains(j))
                    {
                        CloudsCollectionPeakRemoved[i].Add(CloudsCollectionOriginal[i][j]);
                        CircleDiameterList[i].Add(diameter_array[j]);
                    }
                }
            }
        }

        private List<int> GetPeakIndex(List<double> dis, double max_tol = 0.6)
        {
            var ret = new List<int>();
            var len = dis.Count;
            var mean = dis.Average();
            var l = dis.OrderBy(d => Math.Abs(d - mean)).ToList();
            var peak_level = Math.Abs(l[len - 6] - mean);
            for (int i = 1; i < len; i++)
            {
                if (Math.Abs(dis[i] - mean) > peak_level)
                    ret.Add(i);
                if (Math.Abs(dis[i] - dis[i - 1]) > max_tol)
                {
                    ret.Add(i);
                }
                if (i < len - 2)
                {
                    if (Math.Abs(dis[i + 1] - dis[i - 1]) > max_tol)
                    {
                        ret.Add(i + 1);
                    }
                }
                if (i < len - 2)
                {
                    if (Math.Abs(dis[i + 1] - dis[i]) > max_tol)
                        ret.Add(i);
                }
            }
            return ret;
        }

        public void DealRoundness()
        {
            for(int i = 0;i< CloudsCollectionOriginal.Length;i++)
            {
                c[i] = CircleDiameterList[i].Max() - CircleDiameterList[i].Min();
                Console.WriteLine($"Circle[{i}] Roundness: {c[i]}");
            }
        }

        public void GenerateNewCurves()
        {
            int cloud_min_size = CloudsCollectionOriginal.Select(cloud => cloud.Count).ToArray().Min();
            for (int i = 0; i < CloudsCollectionOriginal.Length; i++)
            {
                CloudsOffsetVsRadians[i].Clear();
                for (int j = 0; j < CircleDiameterList[i].Count; j++ )
                { 
                    var p = new CurvePoint();
                    p.p = CircleDiameterList[i][j] - Radius[i];
                    p.Phi = CloudsCollectionPeakRemoved[i][j].Phi;
                    CloudsOffsetVsRadians[i].Add(p);
                }
                // 把8层曲线都按照相同的角度分布重新排布
                CloudsCollectionForFFT[i] = MathHelper.NewCurve(CloudsOffsetVsRadians[i], cloud_min_size);
            }
        }

        public void TestFFT()
        {
            var lines = File.ReadAllLines("curve_calibration.csv");
            List<double> signal = new List<double>();
            foreach (var line in lines)
            {
                string[] values = line.Split(',');
                signal.Add(Convert.ToDouble(values[1]));
            };
            MathHelper.FFTFilter(signal.ToArray());
        }

        public void FilterCurves()
        {
            for(int i = 0; i<CloudsCollectionOriginal.Length;i++)
            {
                var new_offset = MathHelper.FFTFilter(CloudsCollectionForFFT[i].Select(p => p.p).ToArray());
                for (int j = 0; j < CloudsCollectionForFFT[i].Count; j++)
                {
                    var p = CloudsCollectionForFFT[i][j];
                    p.p = new_offset[j] + Radius[i];
                    p.Z = Zmeans[i];
                    p.X = Math.Abs(p.p) * Math.Cos(p.Phi) + CenterPoints[i].X;
                    p.Y = Math.Abs(p.p) * Math.Sin(p.Phi) + CenterPoints[i].Y;
                }
            }
        }

        public void GenerateCenterLine()
        {
            center_p01 = (CenterPoints[0] + CenterPoints[1]) / 2;
            center_p67 = (CenterPoints[6] + CenterPoints[7]) / 2;
            center_line0167.SetPoint(center_p01,center_p67);
            center_line01.SetPoint(CenterPoints[0], CenterPoints[1]);
            center_line23.SetPoint(CenterPoints[2], CenterPoints[3]);
            center_line45.SetPoint(CenterPoints[4], CenterPoints[5]);
            center_line67.SetPoint(CenterPoints[6], CenterPoints[7]);
        }

        public void DealCoaxiality()
        {
            var ci = new List<int> { 0, 1, 6, 7 };
            foreach(var i in ci)
            {
                Coaxialitys[i] = MathHelper.PointToLine3D(center_line0167, CenterPoints[i], ref Pds[i]);
            }
            var tmp = new CurvePoint();
            Coaxialitys[2] = MathHelper.PointToLine3D(center_line67, CenterPoints[0], ref tmp);
            Coaxialitys[3] = MathHelper.PointToLine3D(center_line67, CenterPoints[1], ref tmp);
            g1 = Math.Max(Coaxialitys[6], Coaxialitys[7]) * 2;
            g3 = Math.Max(Coaxialitys[0], Coaxialitys[1]) * 2;
            Coaxiality = Coaxialitys.Max() * 2;
            Console.WriteLine($"Coaxiality: {Coaxiality}");
        }

        public void DealCircleDx()
        {
            center_p23 = (CenterPoints[2] + CenterPoints[3]) / 2;
            center_p45 = (CenterPoints[4] + CenterPoints[5]) / 2;
            CircleDx[0] = MathHelper.PointToLine3D(center_line0167, center_p23, ref Pds[2]);
            CircleDx[1] = MathHelper.PointToLine3D(center_line0167, center_p45, ref Pds[4]);
            Console.WriteLine($"Dx: {CircleDx[0]}, {CircleDx[1]}");
        }

        public void DealCylinder()
        {
            var tmp = new CurvePoint();
            var lines = new CurveLine[8] {
                center_line01,
                center_line01,
                center_line23,
                center_line23,
                center_line45,
                center_line45,
                center_line67,
                center_line67
            };
            double[] maxd = new double[8];
            double[] mind = new double[8];
            foreach (var l in CylinderDiameterList) l.Clear();
            for (int i = 0; i < CloudsCollectionForFFT.Length; i++)
            {
                for (int j = 0; j < CloudsCollectionForFFT[i].Count; j++)
                {
                    CylinderDiameterList[i].Add(MathHelper.PointToLine3D(lines[i], CloudsCollectionForFFT[i][j], ref tmp));
                }
                maxd[i] = CylinderDiameterList[i].Max();
                mind[i] = CylinderDiameterList[i].Min();
            }
            Cylinders[0] = Math.Max(maxd[0], maxd[1]) - Math.Min(mind[0], mind[1]);
            Cylinders[1] = Math.Max(maxd[2], maxd[3]) - Math.Min(mind[2], mind[3]);
            Cylinders[2] = Math.Max(maxd[4], maxd[5]) - Math.Min(mind[4], mind[5]);
            Cylinders[3] = Math.Max(maxd[6], maxd[7]) - Math.Min(mind[6], mind[7]);
            Console.WriteLine($"Diameters: {d4}, {d6}, {d7}, {d5}");
            Console.WriteLine($"Cylinders: {Cylinders[0]}, {Cylinders[1]}, {Cylinders[2]}, {Cylinders[3]}");
        }

        public void DealAngle()
        {
            var v6 = (center_p23 - Pds[2]).ToVector3D(); // d6偏心向量
            var v7 = (center_p45 - Pds[4]).ToVector3D(); // d7偏心向量

            Vector3D v76 = new Vector3D(v7.X - v6.X, v7.Y - v6.Y, 0);
            Vector3D vZero = new Vector3D(1.0, 0, 0);
            _AngleTwoDeg = MathHelper.GetVectorAngle(v76, vZero);
            AngleTwoDeg = MathHelper.ReduceAngle(_AngleTwoDeg + CAL.AngleTwoOffset, Calibration.GearConst);
            AngleTwoDms = MathHelper.DegreeToDms(AngleTwoDeg);

            _Ang = MathHelper.GetVectorAngle(v6, v7) ;
            Ang = _Ang * CAL.AngleRatio;
            if (Ang > 180)
                Ang = 360 - Ang;
            // 因为测量的时候是零件在转，所以一个在静态坐标系下是（p，theta）的点，需要旋转-theta的角度才会被测到，因此计算出来的角度其实是反向的
            PartPhaseRad[0] = -MathHelper.GetRectangularAngle(v6.X, v6.Y);
            PartPhaseRad[1] = -MathHelper.GetRectangularAngle(v7.X, v7.Y);
            // 2022-06-09 修改，不再用余数的方式去求齿相位，而是用绝对的角度位置来计算，可用于判断零件摆放是否合理
            _PhaseDeg[0] = PartPhaseRad[0] / Math.PI * 180.0;
            PhaseDeg[0] = _PhaseDeg[0] + CAL.phaseOffset[0];
            //PhaseDeg[0] = MathHelper.ReduceAngle(_PhaseDeg[0] + CAL.phaseOffset[0], Calibration.GearConst);
            _PhaseDeg[1] = PartPhaseRad[1] / Math.PI * 180.0;
            PhaseDeg[1] = _PhaseDeg[1] + CAL.phaseOffset[1];
            //PhaseDeg[1] = MathHelper.ReduceAngle(_PhaseDeg[1] + CAL.phaseOffset[1], Calibration.GearConst);
            AngDms = MathHelper.DegreeToDms(Ang);
            Phase0Dms = MathHelper.DegreeToDms(PhaseDeg[0]);
            Phase1Dms = MathHelper.DegreeToDms(PhaseDeg[1]);
            Console.WriteLine($"ANG: {AngDms}, Phase0: {Phase0Dms}, Phase1: {Phase1Dms}"); 
        }

        public void JudgeResults()
        {
            // 0-UUID,
            // 1-Diameter0,
            // 2-Diameter1,
            // 3-Diameter2,
            // 4-Diameter3,
            // 5-CircleDx[0],
            // 6-CircleDx[1],
            // 7-Coaxiality,
            // 8-Cylinders[0],
            // 9-Cylinders[1],
            // 10-Cylinders[2],
            // 11-Cylinders[3],
            // 12-AngDms,
            // 13-Phase0Dms,
            // 14-Phase1Dms
            // 15-AngleTwoDms
            // 16-IsOk
            // 17-Date
            IsValueOk[0] = (int)ValueJudge.FullyOK;
            IsValueOk[13] = (int)ValueJudge.FullyOK;
            IsValueOk[14] = (int)ValueJudge.FullyOK;
            IsValueOk[15] = (int)ValueJudge.FullyOK;
            IsValueOk[16] = (int)ValueJudge.FullyOK;
            IsValueOk[17] = (int)ValueJudge.FullyOK;
            // 公差和名义尺寸，后面也要改到配置文件里面
            double dx_norminal = Tolerance.dx_norminal;
            double dx_tolerance = Tolerance.dx_tolerance;
            double d_up = Tolerance.d_up;
            double d_down = Tolerance.d_down;
            double d2_up = Tolerance.d2_up;
            double d2_down = Tolerance.d2_down;
            double cylinder = Tolerance.cylinder;
            double ang_tol = Tolerance.ang_toleracne;
            int IsDiameterOk(double v, double up, double down, double window = 1.5)
            {
                // window 额外的窗口，增加1.5um
                if (v > down && v < up)
                {
                    return (int)ValueJudge.FullyOK;
                }
                else if (v > down - window && v < up + window)
                {
                    return (int)ValueJudge.Warning;
                }
                else
                    return (int)ValueJudge.NG;
            }
            int BoolToValueJudge(bool v)
            {
                if (v) return (int)ValueJudge.FullyOK;
                else return (int)ValueJudge.NG;
            }
            IsValueOk[1] = IsDiameterOk(d4, d_up, d_down);
            IsValueOk[2] = IsDiameterOk(d6, d2_up, d2_down);
            IsValueOk[3] = IsDiameterOk(d7, d2_up, d2_down);
            IsValueOk[4] = IsDiameterOk(d5, d_up, d_down);
            IsValueOk[5] = BoolToValueJudge(Math.Abs(CircleDx[0] - dx_norminal) < dx_tolerance);
            IsValueOk[6] = BoolToValueJudge(Math.Abs(CircleDx[1] - dx_norminal) < dx_tolerance);
            IsValueOk[7] = BoolToValueJudge(Coaxiality < cylinder);
            IsValueOk[8] = BoolToValueJudge(Cylinders[0] < cylinder);
            IsValueOk[9] = BoolToValueJudge(Cylinders[1] < cylinder);
            IsValueOk[10] = BoolToValueJudge(Cylinders[2] < cylinder);
            IsValueOk[11] = BoolToValueJudge(Cylinders[3] < cylinder);
            IsValueOk[12] = BoolToValueJudge(Math.Abs(Ang - 180) < ang_tol);
        }

        public ResultHandler Deal()
        {
            //必须在加载CSV后执行
            SortClouds();
            PolarToRectangular();
            FitCircle(ref CloudsCollectionOriginal);
            GenerateDiameterList(ref CloudsCollectionOriginal);
            RemovePeak();
            DealRoundness();
            GenerateNewCurves();     // 归一化到601个点
            FilterCurves();     // 用FFT滤波器滤波
            FitCircle(ref CloudsCollectionForFFT);  // 重新拟合圆
            GenerateDiameterList(ref CloudsCollectionForFFT);  // 重新计算圆度
            DealRoundness();
            GenerateCenterLine();
            DealCoaxiality();
            DealCircleDx();
            DealAngle();
            DealCylinder();
            JudgeResults();
            return this;
        }

    }
}
